﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Web.Configuration;
using System.Net.Configuration;
using System.Xml.Linq;
using System.Data.SqlClient;

namespace Zatify.API
{
    public class CommonLib
    {
        public static string DecimalSeparator
        {
            get
            {
                return System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
            }
        }

        public static bool UserCheckToken(string token)
        {
            bool result = false;
            SqlParameter outParam = new SqlParameter() { ParameterName = "@Result", SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Output };
            DataAccess.ExecuteProcedure("spAPI_User_CheckToken", new string[] { "Token" }, new object[] { token }, outParam);

            if (Convert.ToInt32(outParam.Value) == 1)
                result = true;

            return result;
        }

        public static bool CheckToken(string token)
        {
            bool result = false;
            SqlParameter outParam = new SqlParameter() { ParameterName = "@Result", SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Output };
            DataAccess.ExecuteProcedure("spAPI_CheckToken", new string[] { "Token" }, new object[] { token }, outParam);

            if (Convert.ToInt32(outParam.Value) == 1)
                result = true;

            return result;
        }

        public static XDocument ErrorXml(string message, string code)
        {
            return ErrorXml(message, code, null, null);
        }

        public static XDocument ErrorXml(string message, string code, string[] names, string[] values)
        {
            XDocument doc = new XDocument(new XElement("Result",
                    new XElement("Success", "false"),
                    new XElement("Message", message),
                    new XElement("Code", code)));

            if (names != null)
                for (int i = 0; i < names.Length; ++i)
                    doc.Root.Add(new XElement(names[i], values[i]));

            return doc;
        }

        public static string OkXml()
        {
            return CommonLib.OkXml(null, null);
        }

        //public static XDocument OkXml(string[] names, string[] values)
        //{
        //    XDocument doc = new XDocument(new XElement("Result",
        //            new XElement("Success", "true"),
        //            new XElement("Message", "Ok"),
        //            new XElement("Code", "0")));

        //    if (names != null)
        //        for (int i = 0; i < names.Length; ++i)
        //        {
        //            doc.Root.Add(new XElement(names[i], values[i]));
        //        }

        //    return doc;
        //}

        public static string OkXml(string[] names, string[] values)
        {
            StringWriter sw = new StringWriter();
            sw.WriteLine("<Result>");
            sw.WriteLine("<Success>true</Success>");
            sw.WriteLine("<Message>Ok</Message>");
            sw.WriteLine("<Code>0</Code>");

            //XDocument doc = new XDocument(new XElement("Result",
            //        new XElement("Success", "true"),
            //        new XElement("Message", "Ok"),
            //        new XElement("Code", "0")));

            if (names != null)
                for (int i = 0; i < names.Length; ++i)
                {
                    //doc.Root.Add(new XElement(names[i], values[i]));
                    sw.WriteLine("<" + names[i] + ">");
                    sw.WriteLine(values[i]);
                    sw.WriteLine("</" + names[i] + ">");
                }
            sw.WriteLine("</Result>");

            return sw.ToString();
        }

        /// <summary>
        /// Send e-mail via smtp
        /// </summary>
        /// <param name="sendTo">Reciever</param>
        /// <param name="bcc">Reciever hide copy</param>
        /// <param name="subject">Subject</param>
        /// <param name="msgHtml">Message text in html</param>
        public static void SendEmail(string sendTo, string bcc, string subject, string msgHtml)
        {
            SmtpClient client = new SmtpClient();
            client.EnableSsl = true;

            System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration("~/");
            SmtpSection smtpSection = (SmtpSection)config.GetSection("system.net/mailSettings/smtp");

            MailMessage msg = new MailMessage();
            msg.From = new MailAddress(smtpSection.Network.UserName);
            msg.To.Add(sendTo);
            if (bcc != "")
                msg.Bcc.Add(bcc);
            msg.Subject = subject;
            msg.IsBodyHtml = true;
            msg.Body = msgHtml;
            client.Send(msg);
        }

        public static void Delay(int sec)
        {
            DateTime upDate = DateTime.Now.AddSeconds(sec);
            DateTime now = DateTime.Now;

            while (now < upDate)
                now = DateTime.Now;
        }

        public static void AddGoogleGeoLog(string token, string requestType, string googleUrl, string googleAnswer, string googleEx)
        {
            try
            {
                DataAccess.ExecuteProcedure("spAPI_GoogleGeoLog_Add",
                    new string[] { "Token", "RequestType", "GoogleUrl", "GoogleAnswer", "GoogleEx" },
                    new object[] { token, requestType, googleUrl, googleAnswer, googleEx });
            }
            catch
            {
            }

        }

        public static bool GetLatLong(string address, ref decimal lt, ref decimal lng)
        {
            bool result = false;
            string apiUrl = string.Format("http://maps.googleapis.com/maps/api/geocode/xml?address={0}&sensor=false", address);
            string backstr = "";
            string exMessage = "";

            try
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(apiUrl);
                req.Method = "POST";
                req.ContentLength = 0;

                HttpWebResponse res = (HttpWebResponse)req.GetResponse();
                StreamReader sr = new StreamReader(res.GetResponseStream(), System.Text.Encoding.Default);
                backstr = sr.ReadToEnd();

                sr.Close();
                res.Close();

                XDocument doc = XDocument.Parse(backstr);
                var root = doc.Descendants("GeocodeResponse");
                if (root != null)
                {
                    var status = root.ElementAt(0).Descendants("status");
                    if (status != null)
                        if (status.ElementAt(0).Value == "OK")
                        {
                            var geometry = root.ElementAt(0).Descendants("geometry");
                            if (geometry != null)
                            {
                                var latt = geometry.ElementAt(0).Descendants("lat");
                                if (latt != null)
                                {
                                    lt = Convert.ToDecimal(latt.ElementAt(0).Value.Replace(".", DecimalSeparator));

                                    var longt = geometry.ElementAt(0).Descendants("lng");
                                    if (longt != null)
                                    {
                                        lng = Convert.ToDecimal(longt.ElementAt(0).Value.Replace(".", DecimalSeparator));
                                        result = true;
                                    }
                                }
                            }
                        }
                }
            }
            catch(Exception ex)
            {
                exMessage = ex.Message;
            }

            if (!result)
                AddGoogleGeoLog("", "latlong", apiUrl, backstr, exMessage);

            return result;
        }

        public static int GetGmt(decimal lt, decimal lng, out string message)
        {
            message = "";
            int result = -100;
            
            DateTime beginDate = new DateTime(1970, 1, 1);
            DateTime summerDate = new DateTime(2013, 5, 1);
            string summerTimeStamp = (summerDate - beginDate).TotalSeconds.ToString();
            string apiUrl = string.Format("https://maps.googleapis.com/maps/api/timezone/xml?location={0},{1}&timestamp={2}&sensor=false", lt.ToString(), lng.ToString(), summerTimeStamp);

            string backstr = "";
            string exMessage = "";

            try
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(apiUrl);
                req.Method = "POST";
                req.ContentLength = 0;

                HttpWebResponse res = (HttpWebResponse)req.GetResponse();
                StreamReader sr = new StreamReader(res.GetResponseStream(), System.Text.Encoding.Default);
                backstr = sr.ReadToEnd();

                sr.Close();
                res.Close();

                XDocument doc = XDocument.Parse(backstr);
                var root = doc.Descendants("TimeZoneResponse");
                if (root != null)
                {
                    var status = root.ElementAt(0).Descendants("status");
                    if (status != null)
                        if (status.ElementAt(0).Value == "OK")
                        {
                            var utcOffset = root.ElementAt(0).Descendants("raw_offset");
                            if (utcOffset != null)
                            {
                                int offset = 0;
                                var dstOffset = root.ElementAt(0).Descendants("dst_offset");
                                if (dstOffset != null)
                                    offset = Convert.ToInt32(Convert.ToDecimal(dstOffset.ElementAt(0).Value.Replace(".", DecimalSeparator))) / 60 / 60;
                                
                                result = Convert.ToInt32(Convert.ToDecimal(utcOffset.ElementAt(0).Value.Replace(".", DecimalSeparator))) / 60 / 60 + offset;
                            }
                        }
                }

                if ((result == -100) && (string.IsNullOrEmpty(message)))
                    message = "Error google answer parse";
            }
            catch(Exception e)
            {
                exMessage = message = e.Message;
            }

            if (result == -100)
                AddGoogleGeoLog("", "gmt", apiUrl, backstr, exMessage);

            return result;
        }

        public static string PayPromotion(string token, int promotionId, decimal amount, out decimal summaForPay, out decimal summaFromRest)
        {
            summaForPay = 0;
            summaFromRest = 0;

            try
            {
                // try for pay
                DataTable dt = DataAccess.GetDataTable("spAPI_Promotion_GetPayInfo",
                    new string[] { "Token", "PromotionId" },
                    new object[] { token, promotionId });

                switch (Convert.ToInt32(dt.Rows[0]["IdResult"]))
                {
                    case -1:
                        return CommonLib.ErrorXml("Promotion not found", "2").ToString();
                    case 0:
                        return CommonLib.ErrorXml("Invalid token", "1").ToString();
                }

                if (Convert.ToDecimal(dt.Rows[0]["CampaignBudget"]) < Convert.ToDecimal(dt.Rows[0]["RatePerPush"]) + Convert.ToDecimal(dt.Rows[0]["FixedCost"]))
                    return CommonLib.ErrorXml("Insufficient budget", "11").ToString();

                string payMessage = "";
                if (amount == -1)
                    amount = Convert.ToDecimal(dt.Rows[0]["CampaignBudget"]);

                summaFromRest = Math.Min(amount, Convert.ToDecimal(dt.Rows[0]["PromoRest"]));
                summaForPay = Math.Max(0, amount - summaFromRest);

                if (summaForPay > 0)
                    if (!PaymentService.Pay(dt.Rows[0]["CreditCardNo"].ToString(), dt.Rows[0]["CreditCardPin"].ToString(),
                                            Convert.ToDateTime(dt.Rows[0]["CreditCardExpDate"]), summaForPay,
                                            dt.Rows[0]["Name"].ToString(), out payMessage))
                        return CommonLib.ErrorXml("Error during payment: " + payMessage, "8").ToString();
            }
            catch (Exception ex)
            {
                return CommonLib.ErrorXml(ex.Message, "100").ToString();
            }

            return "";
        }
    }

    public class ImageResult : ActionResult
    {
        public ImageResult(Stream imageStream, string contentType)
        {
            if (imageStream == null)
                throw new ArgumentNullException("imageStream");
            if (contentType == null)
                throw new ArgumentNullException("contentType");

            this.ImageStream = imageStream;
            this.ContentType = contentType;
        }

        public Stream ImageStream { get; private set; }
        public string ContentType { get; private set; }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            HttpResponseBase response = context.HttpContext.Response;

            response.ContentType = this.ContentType;

            byte[] buffer = new byte[4096];
            while (true)
            {
                int read = this.ImageStream.Read(buffer, 0, buffer.Length);
                if (read == 0)
                    break;

                response.OutputStream.Write(buffer, 0, read);
            }

            response.End();
        }
    }

    public static class ControllerExtensions
    {
        public static ImageResult Image(this Controller controller, Stream imageStream, string contentType)
        {
            return new ImageResult(imageStream, contentType);
        }

        public static ImageResult Image(this Controller controller, byte[] imageBytes, string contentType)
        {
            return new ImageResult(new MemoryStream(imageBytes), contentType);
        }
    }


}